package fuck.hookwzry.qq;

import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Base64;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.util.TreeMap;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import oicq.wlogin_sdk.request.WloginAllSigInfo;
import oicq.wlogin_sdk.sharemem.WloginSigInfo;

import android.util.Log;
import android.widget.Toast;

import com.alibaba.fastjson.JSONObject;

public class FileRead {
    @SuppressLint("SdCardPath")
    private static final String qqPath = "/data/data/com.tencent.mobileqq";
    private static final String imeiName = "/files/qm/spread_data";
    private static final String guidName = "/files/wlogin_device.dat";
    private static final String tkkName = "/databases/tk_file";
    public static boolean useLocalFile = false;

    /**
     * 获取IMEI
     *
     * @param context context
     * @return IMEI
     * @throws IOException 异常
     */
    public static String readQimei(Context context, Boolean needToast) throws IOException {
        if (useLocalFile) {
            // 确保数据库文件已经被复制到内部存储
            copyDatabaseFromAssets(context, "spread_data");

            // 打开数据库
            String dbPath = context.getDatabasePath("spread_data").getAbsolutePath();

            String spreadData = readToString(dbPath, context, needToast);
            return decode(Base64.decode(spreadData, 2));
        } else {
            String spreadData = readToString(qqPath + imeiName, context, needToast);
            return decode(Base64.decode(spreadData, 2));
        }
    }

    /**
     * 获取Guid
     *
     * @param context context
     * @return Guid
     * @throws IOException 异常
     */
    public static String readGuid(Context context, Boolean needToast) throws IOException {
        if (useLocalFile) {
            // 确保数据库文件已经被复制到内部存储
            copyDatabaseFromAssets(context, "wlogin_device.dat");

            // 打开数据库
            String dbPath = context.getDatabasePath("wlogin_device.dat").getAbsolutePath();
            return util.buf_to_string(readFileToByteArray(dbPath, context, needToast));
        } else {
            return util.buf_to_string(readFileToByteArray(qqPath + guidName, context, needToast));
        }
    }

    /**
     * 获取Token
     *
     * @param guid guid
     * @return Token
     * @throws Exception 异常
     */
    public static JSONObject getTk(Context context, String guid, Boolean needToast) throws Exception {
        JSONObject ticket = new JSONObject();
        TreeMap treeMap = getTkk(context, guid, needToast);
        if (treeMap == null) {
            if (needToast){
                Toast.makeText(context, "treeMap获取失败", Toast.LENGTH_SHORT).show();
            }
            return null;
        }
        Long account = Long.valueOf(treeMap.firstKey().toString());
        Log.i("FileRead", "account " + account);

        ticket.put("qq", account);
        WloginAllSigInfo wloginAllSigInfo = (WloginAllSigInfo) treeMap.get(account);

        for (Long key : wloginAllSigInfo._tk_map.keySet()) {
            WloginSigInfo wloginSigInfo = wloginAllSigInfo._tk_map.get(key);
            if (wloginSigInfo._TGT == null || wloginSigInfo._TGT.length == 0) {
                continue;
            }
            String d2 = util.buf_to_string(wloginSigInfo._D2);
            String d2Key = util.buf_to_string(wloginSigInfo._D2Key);
            String tgt = util.buf_to_string(wloginSigInfo._TGT);
            String tgtKey = util.buf_to_string(wloginSigInfo._TGTKey);
            String deviceToken = util.buf_to_string(wloginSigInfo._device_token);

            Log.i("FileRead", "d2 " + d2);
            Log.i("FileRead", "d2Key " + d2Key);
            Log.i("FileRead", "tgt " + tgt);
            Log.i("FileRead", "tgtKey " + tgtKey);
            Log.i("FileRead", "deviceToken " + deviceToken);

            ticket.put("d2", d2);
            ticket.put("d2Key", d2Key);
            ticket.put("deviceToken", deviceToken);
            ticket.put("tgt", tgt);
            ticket.put("tgtKey", tgtKey);
        }
        return ticket;
    }

    /**
     * 通过guid 获取tkk
     *
     * @param guid guid
     * @return tkk
     * @throws Exception 异常
     */
    private static TreeMap<Long, WloginSigInfo> getTkk(Context context, String guid, Boolean needToast) throws Exception {
        SQLiteDatabase database = null;
        if (useLocalFile) {
            // 确保数据库文件已经被复制到内部存储
            copyDatabaseFromAssets(context, "tk_file");

            // 打开数据库
            String dbPath = context.getDatabasePath("tk_file").getAbsolutePath();
            database = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY);
        } else {
            database = openDatabase(context, qqPath + tkkName, needToast);
        }

        if (database == null) {
            Toast.makeText(context, "加密文件失败，请重试！", Toast.LENGTH_SHORT).show();
            return null;
        }

        TreeMap<Long, WloginSigInfo> treeMap = null;
        try (Cursor cursor = database.query("tk_file", new String[]{"tk_file"}, "ID=0", null, null, null, null)) {
            if (cursor != null && cursor.moveToFirst()) {
                byte[] bytes = cursor.getBlob(0);
                byte[] result = cryptor.decrypt(bytes, 0, bytes.length, util.string_to_buf(guid));

                if (result != null) {
                    if (needToast) {
                        Toast.makeText(context, "Result 获取成功", Toast.LENGTH_SHORT).show();
                    }
                    try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(result);
                         ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)) {
                        treeMap = (TreeMap<Long, WloginSigInfo>) objectInputStream.readObject();
                    } catch (Exception e) {
                        if (needToast) {
                            Toast.makeText(context, "Result 读取失败: " + e.getMessage(), Toast.LENGTH_SHORT).show();
                        }
                    }
                } else {
                    if (needToast) {
                        Toast.makeText(context, "Result 获取失败", Toast.LENGTH_SHORT).show();
                    }
                }
            } else {
                if (needToast) {
                    Toast.makeText(context, "Cursor 无数据", Toast.LENGTH_SHORT).show();
                }
            }
        } catch (Exception e) {
            if (needToast) {
                Toast.makeText(context, "数据库查询错误: " + e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }
        return treeMap;
    }

    /**
     * 拷贝文件到本机
     *
     * @param context context
     * @param dbName  文件名
     * @throws IOException 异常
     */
    private static void copyDatabaseFromAssets(Context context, String dbName) throws IOException {
        File dbFile = context.getDatabasePath(dbName);
        if (!dbFile.exists()) {
            try (InputStream is = context.getAssets().open(dbName);
                 OutputStream os = new FileOutputStream(dbFile)) {
                byte[] buffer = new byte[1024];
                int length;
                while ((length = is.read(buffer)) > 0) {
                    os.write(buffer, 0, length);
                }
                os.flush();
            }
        }
    }

    /**
     * 读取数据库数据
     *
     * @param context context
     * @param dbPath  数据库路径
     * @return 数据
     */
    public static SQLiteDatabase openDatabase(Context context, String dbPath, Boolean needToast) {
        File dbFile = new File(dbPath);

        if (!dbFile.exists()) {
            // 文件不存在时的处理逻辑
            if (needToast) {
                Toast.makeText(context, "tkk文件不存在!", Toast.LENGTH_SHORT).show();
            }
            return null;
        }

        try {
            if (needToast) {
                Toast.makeText(context, "tkk文件存在!", Toast.LENGTH_SHORT).show();
            }
            return SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY);
        } catch (Exception e) {
            if (needToast) {
                Toast.makeText(context, "打开tkk文件失败: " + e.getMessage(), Toast.LENGTH_LONG).show();
            }
            return null;
        }
    }

    /**
     * 读取文件
     *
     * @param filePath 文件路径
     * @param context  context
     * @return 文件bytes
     * @throws IOException 异常
     */
    public static byte[] readFileToByteArray(String filePath, Context context, Boolean needToast) throws IOException {
        File file = new File(filePath);
        if (!file.exists()) {
            if (needToast) {
                Toast.makeText(context, "文件不存在: " + filePath, Toast.LENGTH_SHORT).show();
            }
            return new byte[0];
        }
        try (FileInputStream fis = new FileInputStream(file);
             ByteArrayOutputStream bos = new ByteArrayOutputStream()) {

            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
            return bos.toByteArray();
        }
    }

    public static String key = "qAucMjh7lsNepLGw";

    public static String decode(byte[] text) {
        try {
            SecretKeySpec skySpec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivParameterSpec = new IvParameterSpec(key.getBytes());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, skySpec, ivParameterSpec);
            return new String(cipher.doFinal(text));
        } catch (Exception e) {
            Log.e("FileRead", e.toString());
        }
        return null;
    }

    /**
     * 从文件中读取解密后的内容
     *
     * @param filePath 文件路径
     * @param context  context
     * @return 解密内容
     */
    public static String readToString(String filePath, Context context, Boolean needToast) {
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        byte[] buffer = new byte[1024];
        String ret = "";
        StringBuilder sBuilder = new StringBuilder();
        int bytesRead;
        try {
            File file = new File(filePath);
            if (file.exists()) {
                fis = new FileInputStream(file);
                bis = new BufferedInputStream(fis);
                while ((bytesRead = bis.read(buffer)) != -1) {
                    sBuilder.append(new String(buffer, 0, bytesRead));
                }
                ret = sBuilder.toString();
            } else {
                if (needToast) {
                    Toast.makeText(context, "文件不存在: " + filePath, Toast.LENGTH_SHORT).show();
                }
            }
        } catch (Exception e) {
            Log.e("FileRead", e.toString());
        } finally {
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    Log.e("FileRead", e.toString());
                }
            }
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    Log.e("FileRead", e.toString());
                }
            }
        }
        return ret;
    }


    /**
     * root 授权结果
     */
    public static class RootPermissionResult {
        public boolean success;
        public String message;

        public RootPermissionResult(boolean success, String message) {
            this.success = success;
            this.message = message;
        }
    }

    /**
     * 获取root权限
     * @param context 上下文
     * @param needToast 是否需要toast
     * @return 是否成功
     */
    public static RootPermissionResult getRootPermission(Context context, boolean needToast) {
        Process process = null;
        DataOutputStream outputStream = null;
        try {
            process = Runtime.getRuntime().exec("hmsu");
            outputStream = new DataOutputStream(process.getOutputStream());
            outputStream.writeBytes("chmod -R 777 " + qqPath + "\n");
            outputStream.flush();
            outputStream.writeBytes("exit\n");
            outputStream.flush();
            process.waitFor();

            if (process.exitValue() == 0) {
                String msg = "权限修改成功";
                Log.i("getRootPermission", msg);
                if (needToast) {
                    Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
                }
                return new RootPermissionResult(true, msg);
            } else {
                String msg = "权限修改失败，进程退出值：" + process.exitValue();
                Log.e("getRootPermission", msg);
                if (needToast) {
                    Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
                }
                return new RootPermissionResult(false, msg);
            }
        } catch (Exception e) {
            String msg = "获取 Root 权限出错: " + e.getMessage();
            Log.e("getRootPermission", msg, e);
            if (needToast) {
                Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
            }
            return new RootPermissionResult(false, msg);
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    Log.e("getRootPermission", "关闭输出流出错", e);
                }
            }
            if (process != null) {
                process.destroy();
            }
        }
    }

    /**
     * 是否有文件的读写权限
     *
     * @return 结果
     */
    public static boolean hasReadAndWritePermissions(Context context, Boolean needToast) {
        File file = new File(qqPath);
        if (!file.exists()) {
            if (needToast) {
                Toast.makeText(context, "请先登录QQ", Toast.LENGTH_SHORT).show();
            }
            return false;
        }
        boolean canRead = file.canRead();
        boolean canWrite = file.canWrite();
        if (needToast) {
            Toast.makeText(context, "读: " + canRead + " 写: " + canWrite, Toast.LENGTH_SHORT).show();
        }
        return canRead && canWrite;
    }
}
